/*******************************************************************************
 * 创建: 2025年06月28日
 * 作者: 水煮肉片饭(27185709@qq.com)
 * 描述: 节点扩展，在3.x中兼容2.x的接口并扩展新功能
*******************************************************************************/
import { Node, Quat, Vec3, TransformBit, UIRenderer, Sprite, Label, Color, UIOpacity, UISkew, UITransform, __private, js, Tween } from 'cc';
import { JSB } from 'cc/env';
const PI180 = Math.PI / 180;
Reflect.defineProperty(Node.prototype, 'x', {
    get: function (): number { return this._lpos.x; },
    set: JSB ? function (val) {
        this.setPosition(val, this._lpos.y, this._lpos.z);
    } : function (val) {
        this._lpos.x = val;
        this.invalidateChildren(TransformBit.POSITION);
    },
});
Reflect.defineProperty(Node.prototype, 'y', {
    get: function (): number { return this._lpos.y; },
    set: JSB ? function (val) {
        this.setPosition(this._lpos.x, val, this._lpos.z);
    } : function (val) {
        this._lpos.y = val;
        this.invalidateChildren(TransformBit.POSITION);
    },
});
Reflect.defineProperty(Node.prototype, 'z', {
    get: function (): number { return this._lpos.z; },
    set: JSB ? function (val) {
        this.setPosition(this._lpos.x, this._lpos.y, val);
    } : function (val) {
        this._lpos.z = val;
        this.invalidateChildren(TransformBit.POSITION);
    },
});
Reflect.defineProperty(Node.prototype, 'scaleX', {
    get: function (): number { return this._lscale.x; },
    set: JSB ? function (val) {
        this.setScale(val, this._lscale.y, this._lscale.z);
    } : function (val) {
        this._lscale.x = val;
        this.invalidateChildren(TransformBit.SCALE);
    },
});
Reflect.defineProperty(Node.prototype, 'scaleY', {
    get: function (): number { return this._lscale.y; },
    set: JSB ? function (val) {
        this.setScale(this._lscale.x, val, this._lscale.z);
    } : function (val) {
        this._lscale.y = val;
        this.invalidateChildren(TransformBit.SCALE);
    },
});
Reflect.defineProperty(Node.prototype, 'scaleZ', {
    get: function (): number { return this._lscale.z; },
    set: JSB ? function (val) {
        this.setScale(this._lscale.x, this._lscale.y, val);
    } : function (val) {
        this._lscale.z = val;
        this.invalidateChildren(TransformBit.SCALE);
    },
});
Reflect.defineProperty(Node.prototype, 'scaleXY', {
    get: function (): number { return this._lscale.x; },
    set: JSB ? function (val) {
        this.setScale(val, val, this._lscale.z);
    } : function (val) {
        this._lscale.x = this._lscale.y = val;
        this.invalidateChildren(TransformBit.SCALE);
    },
});
Reflect.defineProperty(Node.prototype, 'skewX', {
    get: function (): number { return this.getComponent(UISkew)?._skew.x ?? 0; },
    set: JSB ? function (val) {
        let us = this.getComponent(UISkew) ?? this.addComponent(UISkew);
        this._setSkewX(us._skew.x = val);
        us._updateNodeTransformFlags();
    } : function (val) {
        let us = this.getComponent(UISkew) ?? this.addComponent(UISkew);
        us._skew.x = val;
        us._updateNodeTransformFlags();
    },
});
Reflect.defineProperty(Node.prototype, 'skewY', {
    get: function (): number { return this.getComponent(UISkew)?._skew.y ?? 0; },
    set: JSB ? function (val) {
        let us = this.getComponent(UISkew) ?? this.addComponent(UISkew);
        this._setSkewY(us._skew.y = val);
        us._updateNodeTransformFlags();
    } : function (val) {
        let us = this.getComponent(UISkew) ?? this.addComponent(UISkew);
        us._skew.y = val;
        us._updateNodeTransformFlags();
    },
});
Reflect.defineProperty(Node.prototype, 'angle', {
    get: function (): number { return this._euler.z; },
    set: JSB ? function (val) {
        this.setRotationFromEuler(0, 0, val);
    } : function (val) {
        Quat.fromAngleZ(this._lrot, this._euler.z = val);
        this.invalidateChildren(TransformBit.ROTATION);
    },
});
Reflect.defineProperty(Node.prototype, 'radian', {
    get: function (): number { return this._euler.z * PI180; },
    set: JSB ? function (val) {
        this.setRotationFromEuler(0, 0, val / PI180);
    } : function (val) {
        Quat.fromAngleZ(this._lrot, this._euler.z = val / PI180);
        this.invalidateChildren(TransformBit.ROTATION);
    },
});
Reflect.defineProperty(Node.prototype, 'color', {
    get: function (): Color { return this.getComponent(UIRenderer)?._color ?? new Color(255, 255, 255); },
    set: function (val) {
        let ur = this.getComponent(UIRenderer) ?? this.addComponent(UIRenderer);
        ur._color = val;
        ur._updateColor();
    },
});
Reflect.defineProperty(Node.prototype, 'opacity', {
    get: function (): number {
        let uo = this.getComponent(UIOpacity);
        if (uo) { return uo.opacity; }
        let ur = this.getComponent(Sprite) || this.getComponent(Label);
        if (ur) { return ur.color.a; }
        return 255;
    },
    set: function (val) {
        let uo = this.getComponent(UIOpacity);
        if (uo) { uo.opacity = val; return; }
        let ur = this.getComponent(Sprite) || this.getComponent(Label);
        if (ur) { ur.color.a = val; ur._updateColor(); return; }
        this.addComponent(UIOpacity).opacity = val;
    },
});
Reflect.defineProperty(Node.prototype, 'widthU', {
    get: function (): number { return this.getComponent(UITransform)?._contentSize.width ?? 100; },
    set: function (val) {
        let ut = this.getComponent(UITransform) ?? this.addComponent(UITransform);
        ut._contentSize.width = val;
        ut._markRenderDataDirty();
    },
});
Reflect.defineProperty(Node.prototype, 'heightU', {
    get: function (): number { return this.getComponent(UITransform)?._contentSize.height ?? 100; },
    set: function (val) {
        let ut = this.getComponent(UITransform) ?? this.addComponent(UITransform);
        ut._contentSize.height = val;
        ut._markRenderDataDirty();
    },
});
Reflect.defineProperty(Node.prototype, 'anchorXU', {
    get: function (): number { return this.getComponent(UITransform)?._anchorPoint.x ?? 0.5; },
    set: function (val) {
        let ut = this.getComponent(UITransform) ?? this.addComponent(UITransform);
        ut._anchorPoint.x = val;
        ut._markRenderDataDirty();
    },
});
Reflect.defineProperty(Node.prototype, 'anchorYU', {
    get: function (): number { return this.getComponent(UITransform)?._anchorPoint.y ?? 0.5; },
    set: function (val) {
        let ut = this.getComponent(UITransform) ?? this.addComponent(UITransform);
        ut._anchorPoint.y = val;
        ut._markRenderDataDirty();
    },
});
Reflect.defineProperty(Node.prototype, 'get', {
    value: function <T = Node>(hierarchy: string, typeOrClassName?: __private.__types_globals__Constructor<T> | string): T | null {
        let node = this;
        if (hierarchy) {
            for (let url = hierarchy.split('/'), i = 0, len = url.length; i < len; i++) {
                node = node.getChildByName(url[i]);
                if (!node) return null;
            }
        }
        let type: any = typeOrClassName ?? Node;
        if (typeof type === 'string') {
            type = js.getClassByName(type.startsWith('cc.') ? type : `cc.${type}`);
            if (!type) return null;
        }
        return type === Node ? node : Node['_findComponent'](node, type) as T | null;
    },
});
Reflect.defineProperty(Node.prototype, 'find', {
    value: function (name: string, isStartWith: boolean = false): Node[] {
        let ret = [];
        let match = isStartWith
            ? (node: Node) => node.name.startsWith(name)
            : (node: Node) => node.name === name;
        let find = (node: Node) => {
            for (let i = 0, children = node.children, len = children.length; i < len; i++) {
                let child = children[i];
                match(child) && ret.push(child);
                find(child);
            }
        }
        find(this);
        return ret;
    },
});
Reflect.defineProperty(Node.prototype, 'convert', {
    value: function (pos: Vec3, target?: Node): Vec3 {
        let ut = this.getComponent(UITransform) ?? this.addComponent(UITransform);
        pos = ut.convertToWorldSpaceAR(pos);
        return target ? target.inverseTransformPoint(pos, pos) : pos;
    },
});
Reflect.defineProperty(Node.prototype, 'setColor', {
    value: function (time: number, color?: Color): Promise<void> {
        return new Promise<void>((resolve) => {
            for (let urs = this.getComponentsInChildren(UIRenderer), i = urs.length - 1; i >= 0; i--) {
                let ur = urs[i];
                if (!ur.node.active) continue;
                ur['ColorOriginal'] ??= ur.color.clone();
                ur['ColorTween']?.stop();
                ur['ColorTween'] = new Tween(ur).to(time, { color: color ?? ur['ColorOriginal'] }, {
                    onComplete: () => {
                        if (!color) {
                            delete ur['ColorTween'];
                            delete ur['ColorOriginal'];
                        }
                        i === 0 && resolve();
                    }
                }).start();
            };
        });
    },
});
Reflect.defineProperty(Node.prototype, 'setShake', {
    value: function (repeat: number, amplitude: Vec3 = new Vec3(100, 0), frequency: number = 0.04): Promise<void> {
        return new Promise<void>((resolve) => {
            if (!this.active) { resolve(); return; }
            let pos = this._lpos;
            if (pos['ShakeAmplitude']) {
                this.setPosition(pos.x - pos['ShakeAmplitude'].x, pos.y - pos['ShakeAmplitude'].y);
                clearInterval(pos['ShakeHandle']);
            }
            if (repeat === 0) {
                delete pos['ShakeHandle'];
                delete pos['ShakeAmplitude'];
                resolve(); return;
            }
            (repeat |= 0) === 1 && (repeat = 2);
            let step = repeat > 0 ? new Vec3(-amplitude.x / repeat, -amplitude.y / repeat) : new Vec3();
            pos['ShakeAmplitude'] = amplitude.add(step);
            this.setPosition(pos.x + amplitude.x, pos.y + amplitude.y);
            --repeat < 0 && resolve();
            pos['ShakeHandle'] = setInterval(() => {
                this.setPosition(pos.x - amplitude.x, pos.y - amplitude.y);
                amplitude.negative().add(step.negative());
                this.setPosition(pos.x + amplitude.x, pos.y + amplitude.y);
                if (--repeat === 0) {
                    clearInterval(pos['ShakeHandle']);
                    delete pos['ShakeHandle'];
                    delete pos['ShakeAmplitude'];
                    resolve(); return;
                }
            }, Math.max(frequency, 0.04) * 1000);
        });
    },
});
declare module 'cc' {
    interface Node {
        x: number;
        y: number;
        z: number;
        scaleX: number;
        scaleY: number;
        scaleZ: number;
        scaleXY: number;
        skewX: number;
        skewY: number;
        angle: number;
        radian: number;
        color: Color;
        opacity: number;
        widthU: number;
        heightU: number;
        anchorXU: number;
        anchorYU: number;
        /** 返回层级为hierarchy的子节点的typeOrClassName组件，如果typeOrClassName缺省则返回该子节点 */
        get<T = Node>(hierarchy: string, typeOrClassName?: __private.__types_globals__Constructor<T> | string): T | null;
        /** 返回所有名称为name的子节点，如果isStartWith为true则返回所有名称以name开头的子节点 */
        find(name: string, isStartWith?: boolean): Node[];
        /** 返回当前节点坐标系下的点pos，在target节点坐标系的坐标，target缺省则返回世界坐标下的坐标 */
        convert(pos: Vec3, target?: Node): Vec3;
        /** 节点颜色在time秒内变化到color，如果color缺省则在time秒内恢复初始颜色 */
        setColor(time: number, color?: Color): Promise<void>;
        /** 节点振动repeat次，振幅amplitude（水平、垂直2个方向），振动频率frequency秒
         * repeat < 0：振动w无限次   repeat = 0 停止振动 */
        setShake(repeat: number, amplitude?: Vec3, frequency?: number): Promise<void>;
    }
}